package org.cnam.mobileShopping.wsclient;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.cnam.mobileShopping.bean.Category;
import org.cnam.mobileShopping.bean.MList;
import org.cnam.mobileShopping.bean.Product;
import org.cnam.mobileShopping.bean.Productlist;
import org.cnam.mobileShopping.bean.User;
import org.cnam.mobileShopping.util.IOUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;

public class WSHelper {
	private static final String TAG = "WSHelper";

	private static String getWSIdentifierForClass(Class<?> entityClass) {
		return entityClass.getAnnotation(WSEntityListId.class).value();
	}

	public static void call(String sUrl) throws IOException {
		getResultsAsString(sUrl);
	}

	public static <T> T getEntity(String sUrl, Class<T> entityClass)
			throws IOException, JSONException {
		String sData = getResultsAsString(sUrl);
		Log.d(TAG, sData);
		if ("null".equals(sData)) {
			return null;
		} else {
			return parseSingleResult(sData, entityClass);
		}
	}

	public static <T> List<T> getEntityList(String sUrl, Class<T> entityClass)
			throws IOException, JSONException {
		String sData = getResultsAsString(sUrl);
		Log.d(TAG, sData);
		if ("null".equals(sData)) {
			return null;
		} else {
			return parseListResult(sData, entityClass);
		}
	}

	private static <T> List<T> parseListResult(String sData,
			Class<T> entityClass) throws JSONException {
		Log.d(TAG, "Parsing results");
		JSONObject jsonRoot = new JSONObject(sData);
		String jsonIdentifier = WSHelper.getWSIdentifierForClass(entityClass);
		Log.d(TAG, jsonIdentifier);
		Object arrayOrObject = jsonRoot.get(jsonIdentifier);
		return WSHelper.parseJSONObjectAsList(arrayOrObject, entityClass);
	}

	private static <T> T parseSingleResult(String sData, Class<T> entityClass)
			throws JSONException {
		Log.d(TAG, "Parsing result");
		JSONObject jsonRoot = new JSONObject(sData);
		return WSHelper.parseJSONObject(jsonRoot, entityClass);
	}

	private static String getResultsAsString(String sUrl) throws IOException {
		InputStream inputStream = null;
		ByteArrayOutputStream outputStream = null;
		String sData = null;
		try {
			URL url = new URL(sUrl);
			Log.d("WSHelper", url.toString());
			HttpURLConnection con = (HttpURLConnection) url.openConnection();
			con.setReadTimeout(60000 /* milliseconds */);
			con.setConnectTimeout(60000 /* milliseconds */);
			con.setRequestMethod("GET");
			con.setDoInput(true);

			// Start the query
			con.connect();

			// Read results from the query
			inputStream = con.getInputStream();
			outputStream = new ByteArrayOutputStream();
			IOUtils.copy(inputStream, outputStream);
			sData = new String(outputStream.toByteArray(), "UTF-8");
		} catch (FileNotFoundException ex) {
			Log.e(TAG, "No result");
			sData = "null";
		} catch (IOException ex) {
			Log.e(TAG, ex.getMessage(), ex);
			sData = "null";
		} finally {
			IOUtils.closeQuietly(inputStream);
			IOUtils.closeQuietly(outputStream);
		}
		return sData;
	}

	@SuppressWarnings("unchecked")
	private static <T> T parseJSONObject(JSONObject jsonObject,
			Class<T> entityClass) throws JSONException {
		// music elements...
		if (Category.class.isAssignableFrom(entityClass)) {
			return (T) Category.parseJSON(jsonObject);
		} else if (User.class.isAssignableFrom(entityClass)) {
			return (T) User.parseJSON(jsonObject);
		} else if (Product.class.isAssignableFrom(entityClass)) {
			return (T) Product.parseJSON(jsonObject);
		} else if (MList.class.isAssignableFrom(entityClass)) {
			return (T) MList.parseJSON(jsonObject);
		} else if (Productlist.class.isAssignableFrom(entityClass)) {
			return (T) Productlist.parseJSON(jsonObject);
		}
		// Primitive elements
		else if (String.class.isAssignableFrom(entityClass)) {
			return (T) (String) jsonObject.getString("value");
		} else if (Integer.class.isAssignableFrom(entityClass)) {
			return (T) (Integer) jsonObject.getInt("value");
		} else if (Long.class.isAssignableFrom(entityClass)) {
			return (T) (Long) jsonObject.getLong("value");
		} else if (Double.class.isAssignableFrom(entityClass)) {
			return (T) (Double) jsonObject.getDouble("value");
		}
		// Unknown
		else {
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	private static <T> List<T> parseJSONObjectAsList(Object arrayOrObject,
			Class<T> entityClass) throws JSONException {
		List<T> list = new ArrayList<T>();
		if (arrayOrObject instanceof JSONArray) {
			for (int index = 0; index < ((JSONArray) arrayOrObject).length(); index++) {
				JSONObject jsonObject = ((JSONArray) arrayOrObject)
						.getJSONObject(index);
				list.add(parseJSONObject(jsonObject, entityClass));
			}
		} else if (arrayOrObject instanceof JSONObject) {
			list.add(parseJSONObject((JSONObject) arrayOrObject, entityClass));
		}
		for (T t : list) {
			Log.d(TAG, t.toString());
		}
		return list;
	}
}
